11월 04일 원티드 프리온보딩 백엔드 10일차 TIL
목차
:one: 진행상황
:two: 진행상황리뷰
# views.py
...
from rest_framework_simplejwt.tokens import RefreshToken
...
class LoginView(APIView):
def post(self, request: Request) -> Response:
...
token: Final[RefreshToken] = RefreshToken.for_user(user)
refresh: Final[RefreshToken] = str(token)
access: Final = str(token.access_token)
response = Response({"user": user.id})
response.set_cookie(key="refresh", value=refresh, httponly=True)
response.set_cookie(key="access", value=access, httponly=True)
return response
class LogoutView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
def post(self, request: Request) -> Response:
...
# get token from request cookie
refresh: Final = request.data.get("refresh")
...
try:
# delete token from database
token: Final = RefreshToken(refresh)
token.blacklist()
except Exception:
raise ParseError("Refresh token is invalid.")
# delete token from cookie
response = Response({"status": True})
response.delete_cookie("refresh")
response.delete_cookie("access")
return response
# tests.py
class TestUserView(APITestCase):
...
def test_logout(self):
...
# get tokens
refresh = logged_response.cookies["refresh"].value
access = logged_response.cookies["access"].value
# request logout with tokens
response: Final[Response] = self.client.post(
"/api/v1/users/logout/",
{"refresh": refresh},
HTTP_AUTHORIZATION=f"Bearer {access}",
)
- JWT 생성해 쿠키에 등록하여 로그인 인증하는 코드 작성.
:three: Today I Learned
JWT
- JSON Web Token
- 웹 상에서 로그인 인증을 위한 토큰을 JSON 형식으로 표현한 것
- 헤더(형식 및 알고리즘), 페이로드(내용), 서명(헤더와 페이로드를 합쳐 암호화한 것, 인증시 필요)으로 구성
Django
와 DRF
사용 시 JWT를 쉽게 사용할 수 있도록 도와주는 라이브러리
- 설치:
pip install djangorestframework-simplejwt
- 설정
# config/settings.py
INSTALLED_APPS = [
...
"rest_framework_simplejwt",
'rest_framework_simplejwt.token_blacklist', # 보안을 위해 블랙리스트 사용
...
]
...
EST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
}
REST_USE_JWT = True
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(hours=2),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'TOKEN_USER_CLASS': 'user.User',
}
- 사용
# views.py
from rest_framework_simplejwt.tokens import RefreshToken
# JWT 발급 View(로그인 등)
class CreateJwtView(APIView):
def post(self, request: Request) -> Response:
...
# 유저로부터 refresh 토큰을 생성
token: Final[RefreshToken] = RefreshToken.for_user(user)
# refresh 토큰을 문자열로 변환
refresh: Final[RefreshToken] = str(token)
# refresh 토큰에서 access 토큰을 생성해 문자열로 변환
access: Final = str(token.access_token)
# 응답 생성
response = Response({"data": data})
# 토큰을 쿠키에 등록
response.set_cookie(key="refresh", value=refresh, httponly=True)
response.set_cookie(key="access", value=access, httponly=True)
...
# 응답 반환
return response
# JWT 인증이 필요한 View
class CheckAuteView(APIView):
# 인증 시 JWTAuthentication 사용
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
# JWT 만료 View(로그아웃 등)
class ExpireJwtView(APIView):
...
def post(self, request: Request) -> Response:
...
# 응답에서 refresh 토큰을 가져옴
refresh: Final = request.data.get("refresh")
...
# refresh 토큰을 RefreshToken 객체로 변환
token: Final = RefreshToken(refresh)
# 블랙리스트에 등록
token.blacklist()
# 응답 생성
response = Response({"status": True})
# 쿠키에서 토큰 삭제
response.delete_cookie("refresh")
response.delete_cookie("access")
# 응답 반환
return response
- 테스트 시
- 실 사용시에는 아마 대부분 브라우저에서 처리해줄 듯
# tests.py
class APITestView(APITestCase):
# JWT 인증 필요한 View 테스트
...
def test_get_token(self):
# JWT 발급 View 호출
response = self.client.post(
# JWT 발급 URL,
# JWT 발급 데이터,
)
# 응답 쿠키에서 토큰 추출
self.refresh = logged_response.cookies["refresh"].value
self.access = logged_response.cookies["access"].value
def test_check_token(self):
response: Final[Response] = self.client.post(
# JWT 인증이 필요한 URL,
# JWT 인증이 필요한 데이터,
HTTP_AUTHORIZATION=f"Bearer {self.access}",
)
# HTTP 인증 헤더에 access 토큰을 넣어서 요청